/*
 * bonito north bridge support
 *
 * Copyright (c) 2008 yajin (yajin@vm-kernel.org)
 *
 * This code is licensed under the GNU GPL v2.
 */

/*
fulong 2e mini pc has a bonito north bridge.
In fulong 2f mini pc, bonito north bridge is integrated into ICT/ST loongson 2f cpu.
*/
#include <assert.h>

#include "hw.h"
#include "mips.h"
#include "pci.h"
#include "pc.h"


typedef target_phys_addr_t pci_addr_t;
#include "pci_host.h"

#define DEBUG

#ifdef DEBUG
#define dprintf(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
#else
#define dprintf(fmt, ...)
#endif



/*from linux soure code. include/asm-mips/mips-boards/bonito64.h*/
#define BONITO_BOOT_BASE		0x1fc00000
#define BONITO_BOOT_SIZE		0x00100000
#define BONITO_BOOT_TOP 		(BONITO_BOOT_BASE+BONITO_BOOT_SIZE-1)
#define BONITO_FLASH_BASE		0x1c000000
#define BONITO_FLASH_SIZE		0x03000000
#define BONITO_FLASH_TOP		(BONITO_FLASH_BASE+BONITO_FLASH_SIZE-1)
#define BONITO_SOCKET_BASE		0x1f800000
#define BONITO_SOCKET_SIZE		0x00400000
#define BONITO_SOCKET_TOP		(BONITO_SOCKET_BASE+BONITO_SOCKET_SIZE-1)
#define BONITO_REG_BASE 		0x1fe00000
#define BONITO_REG_SIZE 		0x00040000
#define BONITO_REG_TOP			(BONITO_REG_BASE+BONITO_REG_SIZE-1)
#define BONITO_DEV_BASE 		0x1ff00000
#define BONITO_DEV_SIZE 		0x00100000
#define BONITO_DEV_TOP			(BONITO_DEV_BASE+BONITO_DEV_SIZE-1)
#define BONITO_PCILO_BASE		0x10000000
#define BONITO_PCILO_BASE_VA    0xb0000000
#define BONITO_PCILO_SIZE		0x0c000000
#define BONITO_PCILO_TOP		(BONITO_PCILO_BASE+BONITO_PCILO_SIZE-1)
#define BONITO_PCILO0_BASE		0x10000000
#define BONITO_PCILO1_BASE		0x14000000
#define BONITO_PCILO2_BASE		0x18000000
#define BONITO_PCIHI_BASE		0x20000000
#define BONITO_PCIHI_SIZE		0x20000000
#define BONITO_PCIHI_TOP		(BONITO_PCIHI_BASE+BONITO_PCIHI_SIZE-1)
#define BONITO_PCIIO_BASE		0x1fd00000
#define BONITO_PCIIO_BASE_VA		0xbfd00000
#define BONITO_PCIIO_SIZE		0x00010000
#define BONITO_PCIIO_TOP		(BONITO_PCIIO_BASE+BONITO_PCIIO_SIZE-1)
#define BONITO_PCICFG_BASE		0x1fe80000
#define BONITO_PCICFG_SIZE		0x00080000
#define BONITO_PCICFG_TOP		(BONITO_PCICFG_BASE+BONITO_PCICFG_SIZE-1)




#define BONITO_PCICONFIGBASE		0x00
#define BONITO_REGBASE			0x100

#define BONITO_PCICONFIG_BASE  (BONITO_PCICONFIGBASE+BONITO_REG_BASE)
#define BONITO_PCICONFIG_SIZE  (0x100)

#define BONITO_INTERNAL_REG_BASE  (BONITO_REGBASE+BONITO_REG_BASE)
#define BONITO_INTERNAL_REG_SIZE  (0x70)

#define BONITO_SPCICONFIG_BASE  (BONITO_PCICFG_BASE)
#define BONITO_SPCICONFIG_SIZE  (BONITO_PCICFG_SIZE)



/* 1. Bonito h/w Configuration */
/* Power on register */

#define BONITO_BONPONCFG		( 0x00>>2)      /*0x100 */
#define BONITO_BONGENCFG_OFFSET         0x4
#define BONITO_BONGENCFG		( BONITO_BONGENCFG_OFFSET>>2)   /*0x104 */

/* 2. IO & IDE configuration */
#define BONITO_IODEVCFG 		( 0x08>>2)      /*0x108 */


/* 3. IO & IDE configuration */
#define BONITO_SDCFG			( 0x0c>>2)      /*0x10c */

/* 4. PCI address map control */
#define BONITO_PCIMAP			( 0x10>>2)      /*0x110 */
#define BONITO_PCIMEMBASECFG		( 0x14>>2)      /*0x114 */
#define BONITO_PCIMAP_CFG		( 0x18>>2)      /*0x118 */

/* 5. ICU & GPIO regs */
/* GPIO Regs - r/w */
#define BONITO_GPIODATA_OFFSET          0x1c
#define BONITO_GPIODATA 		( BONITO_GPIODATA_OFFSET>>2)    /*0x11c */
#define BONITO_GPIOIE			( 0x20>>2)      /*0x120 */


/* ICU Configuration Regs - r/w */

#define BONITO_INTEDGE			( 0x24>>2)      /*0x124 */
#define BONITO_INTSTEER 		( 0x28>>2)      /*0x128 */
#define BONITO_INTPOL			( 0x2c>>2)      /*0x12c */

/* ICU Enable Regs - IntEn & IntISR are r/o. */

#define BONITO_INTENSET 		( 0x30>>2)      /*0x130 */
#define BONITO_INTENCLR 		( 0x34>>2)      /*0x134 */
#define BONITO_INTEN			( 0x38>>2)      /*0x138 */
#define BONITO_INTISR			( 0x3c>>2)      /*0x13c */

/* PCI mail boxes */

#define BONITO_PCIMAIL0_OFFSET          0x40
#define BONITO_PCIMAIL1_OFFSET          0x44
#define BONITO_PCIMAIL2_OFFSET          0x48
#define BONITO_PCIMAIL3_OFFSET          0x4c
#define BONITO_PCIMAIL0 		( 0x40>>2)      /*0x140 */
#define BONITO_PCIMAIL1 		( 0x44>>2)      /*0x144 */
#define BONITO_PCIMAIL2 		( 0x48>>2)      /*0x148 */
#define BONITO_PCIMAIL3 		( 0x4c>>2)      /*0x14c */

/* 6. PCI cache */

#define BONITO_PCICACHECTRL		( 0x50>>2)      /*0x150 */
#define BONITO_PCICACHETAG		( 0x54>>2)      /*0x154 */
#define BONITO_PCIBADADDR		( 0x58>>2)      /*0x158 */
#define BONITO_PCIMSTAT 		( 0x5c>>2)      /*0x15c */

/* 7. other*/
#define BONITO_TIMECFG		( 0x60>>2)      /*0x160 */
#define BONITO_CPUCFG		( 0x64>>2)      /*0x164 */
#define BONITO_DQCFG		( 0x68>>2)      /*0x168 */
#define BONITO_MEMSIZE		( 0x6C>>2)      /*0x16c */


#define BONITO_REGS 	(0x70>>2)


/* PCI config for south bridge. type 0 */
#define BONITO_PCICONF_IDSEL_MASK        0xfffff000     /*[31:11] */
#define BONITO_PCICONF_IDSEL_OFFSET        11
#define BONITO_PCICONF_FUN_MASK        0x700    /*[10:8] */
#define BONITO_PCICONF_FUN_OFFSET        8
#define BONITO_PCICONF_REG_MASK        0xFF    
#define BONITO_PCICONF_REG_OFFSET        0


/* idsel BIT = pci slot number +12 */
#define PCI_SLOT_BASE              12
#define PCI_IDSEL_VIA686B_BIT       (17)
#define PCI_IDSEL_VIA686B       (1<<PCI_IDSEL_VIA686B_BIT)

#define DEVFN(slot,fun) (((slot)<<3)+((fun)&0x7))

#define PCI_ADDR(busno,devno,funno,regno)   \
						( (((busno)<<16)&0xff0000) +   (((devno)<<11)&0xf800) + (((funno)<<8)&0x700)  + (regno))


typedef PCIHostState BonitoPCIState;


typedef struct bonitoState
{
    //BonitoPCIState *pci;
    PCIHostState *pci;
    uint32_t regs[BONITO_REGS];

    /*north brige pci config */
    uint8_t config[256];


    /*Bonito registers */
    target_phys_addr_t bonito_reg_start;
    target_phys_addr_t bonito_reg_length;
    int bonito_reg_handle;

    target_phys_addr_t bonito_pciconf_start;
    target_phys_addr_t bonito_pciconf_length;
    int bonito_pciconf_handle;

    target_phys_addr_t bonito_spciconf_start;
    target_phys_addr_t bonito_spciconf_length;
    int bonito_spciconf_handle;


    target_phys_addr_t bonito_pciio_start;
    target_phys_addr_t bonito_pciio_length;
    int bonito_pciio_handle;




} bonito_state;



static void bonito_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    printf("bonito_writeb %llx val %x  \n", addr, val);
    exit(1);
}

static void bonito_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    printf("bonito_writew %llx val %x  \n", addr, val);
    exit(1);
}

static void bonito_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    bonito_state *s = opaque;
    uint32_t saddr;

    saddr = (addr - s->bonito_reg_start) >> 2;

    printf("bonito_writel %llx val %x saddr %x \n", addr, val, saddr);
    switch (saddr)
    {
    case BONITO_BONPONCFG:
    case BONITO_BONGENCFG:
    case BONITO_IODEVCFG:
    case BONITO_SDCFG:
    case BONITO_PCIMAP:
    case BONITO_PCIMEMBASECFG:
    case BONITO_PCIMAP_CFG:
    case BONITO_GPIODATA:
    case BONITO_GPIOIE:
    case BONITO_INTEDGE:
    case BONITO_INTSTEER:
    case BONITO_INTPOL:
    case BONITO_PCIMAIL0:
    case BONITO_PCIMAIL1:
    case BONITO_PCIMAIL2:
    case BONITO_PCIMAIL3:
    case BONITO_PCICACHECTRL:
    case BONITO_PCICACHETAG:
    case BONITO_PCIBADADDR:
    case BONITO_PCIMSTAT:
    case BONITO_TIMECFG:
    case BONITO_CPUCFG:
    case BONITO_DQCFG:
    case BONITO_MEMSIZE:
        s->regs[saddr] = val;
        break;
    case BONITO_INTENSET:
        s->regs[BONITO_INTEN] |= val;
        break;

    case BONITO_INTENCLR:
        s->regs[BONITO_INTEN] &= ~val;
        break;
    case BONITO_INTEN:
    case BONITO_INTISR:
        printf("write to readonly bonito register %x \n", saddr);
        break;
    default:
        printf("write to unknown bonito register %x \n", saddr);
        break;

    }

}

static uint32_t bonito_readb(void *opaque, target_phys_addr_t addr)
{
    printf("bonito_readb %llx  \n", addr);
    exit(1);
}

static uint32_t bonito_readw(void *opaque, target_phys_addr_t addr)
{
    printf("bonito_readw %llx  \n", addr);
    exit(1);

}

static uint32_t bonito_readl(void *opaque, target_phys_addr_t addr)
{
    bonito_state *s = opaque;
    uint32_t saddr;

    saddr = (addr - s->bonito_reg_start) >> 2;

    printf("bonito_readl %llx  \n", addr);

    return s->regs[saddr];
}

static CPUWriteMemoryFunc *bonito_write[] = {
    &bonito_writeb,
    &bonito_writew,
    &bonito_writel,
};

static CPUReadMemoryFunc *bonito_read[] = {
    &bonito_readb,
    &bonito_readw,
    &bonito_readl,
};



static void bonito_pciconf_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    /*bonito_state *s = opaque;
       uint32_t saddr;

       saddr = (addr -s->bonito_pciconf_start ) ;
       s->config[saddr]=(uint8_t)val;
     */
    printf("bonito_pciconf_writeb %llx val %x \n", addr, val);
    exit(1);

}
static void bonito_pciconf_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    /*bonito_state *s = opaque;
       uint32_t saddr;
       uint16_t *ptr=(uint16_t *)s->config;

       saddr = (addr -s->bonito_pciconf_start ) >> 1;

       ptr += saddr;
       *ptr=(uint16_t)(val); */
    printf("bonito_pciconf_writew %llx val %x  \n", addr, val);
    exit(1);

}

static void bonito_pciconf_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    bonito_state *s = opaque;
    uint32_t saddr;
    uint32_t *ptr = (uint32_t *) s->config;

    saddr = (addr - s->bonito_pciconf_start) >> 2;
    printf("bonito_pciconf_writel %llx val %x saddr %x \n", addr, val, saddr);

    ptr += saddr;
    *ptr = val;
}

static uint32_t bonito_pciconf_readb(void *opaque, target_phys_addr_t addr)
{
    printf("bonito_pciconf_readb %llx\n", addr);
    exit(1);
}
static uint32_t bonito_pciconf_readw(void *opaque, target_phys_addr_t addr)
{
    printf("bonito_pciconf_readw %llx\n", addr);
    exit(1);
}

static uint32_t bonito_pciconf_readl(void *opaque, target_phys_addr_t addr)
{

    bonito_state *s = opaque;
    uint32_t saddr;
    uint32_t *ptr = (uint32_t *) s->config;

    saddr = (addr - s->bonito_pciconf_start) >> 2;
    printf("bonito_pciconf_readl %llx\n", addr);

    ptr += saddr;

    return (*ptr);
}

/*north bridge PCI configure space. 0x1fe0 0000 - 0x1fe0 00ff */
static CPUWriteMemoryFunc *bonito_pciconf_write[] = {
    &bonito_pciconf_writeb,
    &bonito_pciconf_writew,
    &bonito_pciconf_writel,
};

static CPUReadMemoryFunc *bonito_pciconf_read[] = {
    &bonito_pciconf_readb,
    &bonito_pciconf_readw,
    &bonito_pciconf_readl,
};



static uint32_t bonito_sbridge_pciaddr(void *opaque, target_phys_addr_t addr)
{
    bonito_state *s = opaque;
    uint32_t cfgaddr;
    uint32_t idsel;
    uint32_t devno;
    uint32_t funno;
    uint32_t regno;
    uint32_t pciaddr;

    /*support type0 pci config */
    assert((s->regs[BONITO_PCIMAP_CFG] & 0x10000) == 0x0);

    cfgaddr = addr & 0xffff;
    cfgaddr += (s->regs[BONITO_PCIMAP_CFG] & 0xffff) << 16;

    idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> BONITO_PCICONF_IDSEL_OFFSET;
    idsel = ffs(idsel) - 1;
    devno = idsel - PCI_SLOT_BASE;
    funno = (cfgaddr & BONITO_PCICONF_FUN_MASK) >> BONITO_PCICONF_FUN_OFFSET;
    regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET;

    if (idsel == 0)
    {
        fprintf(stderr, "error in bonito pci config address " TARGET_FMT_lx "\n", addr);
        exit(1);
    }
    pciaddr = PCI_ADDR(pci_bus_num(s->pci->bus), devno, funno, regno);
    printf("cfgaddr %x devno %x funno %x regno %x \n", cfgaddr, devno, funno, regno);

    return pciaddr;


}

static void bonito_spciconf_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    bonito_state *s = opaque;
    uint32_t pciaddr;
    printf("bonito_spciconf_writeb %llx val %x \n", addr, val);

    pciaddr = bonito_sbridge_pciaddr(s, addr);

    /*set the pci address in s->config_reg */
    s->pci->config_reg = (pciaddr) | (1u << 31);
    pci_host_data_writeb(s->pci, 0, val & 0xff);
}
static void bonito_spciconf_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    bonito_state *s = opaque;
    uint32_t pciaddr;
    printf("bonito_spciconf_writew %llx val %x \n", addr, val);
    assert((addr&0x1)==0);

    pciaddr = bonito_sbridge_pciaddr(s, addr);

    /*set the pci address in s->config_reg */
    s->pci->config_reg = (pciaddr) | (1u << 31);
    pci_host_data_writew(s->pci, 0, val );
}

static void bonito_spciconf_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    bonito_state *s = opaque;
    uint32_t pciaddr;
    printf("bonito_spciconf_writel %llx val %x \n", addr, val);
    assert((addr&0x3)==0);

    pciaddr = bonito_sbridge_pciaddr(s, addr);

    /*set the pci address in s->config_reg */
    s->pci->config_reg = (pciaddr) | (1u << 31);
    pci_host_data_writel(s->pci, 0, val );
}
static uint32_t bonito_spciconf_readb(void *opaque, target_phys_addr_t addr)
{
    bonito_state *s = opaque;
    uint32_t pciaddr;
    printf("bonito_spciconf_readb %llx  \n", addr);

    pciaddr = bonito_sbridge_pciaddr(s, addr);

    /*set the pci address in s->config_reg */
    s->pci->config_reg = (pciaddr) | (1u << 31);
    return pci_host_data_readb(s->pci, 0 );
}

static uint32_t bonito_spciconf_readw(void *opaque, target_phys_addr_t addr)
{
    bonito_state *s = opaque;
    uint32_t pciaddr;
    printf("bonito_spciconf_readw %llx  \n", addr);
    assert((addr&0x1)==0);

    pciaddr = bonito_sbridge_pciaddr(s, addr);

    /*set the pci address in s->config_reg */
    s->pci->config_reg = (pciaddr) | (1u << 31);
    return pci_host_data_readw(s->pci, 0 );
}

static uint32_t bonito_spciconf_readl(void *opaque, target_phys_addr_t addr)
{
    bonito_state *s = opaque;
    uint32_t pciaddr;
    printf("bonito_spciconf_readl %llx  \n", addr);
     assert((addr&0x3)==0);

    pciaddr = bonito_sbridge_pciaddr(s, addr);

    /*set the pci address in s->config_reg */
    s->pci->config_reg = (pciaddr) | (1u << 31);
    return pci_host_data_readl(s->pci, 0 );
}

/*south bridge PCI configure space. 0x1fe8 0000 - 0x1fef ffff */
static CPUWriteMemoryFunc *bonito_spciconf_write[] = {
    &bonito_spciconf_writeb,
    &bonito_spciconf_writew,
    &bonito_spciconf_writel,
};

static CPUReadMemoryFunc *bonito_spciconf_read[] = {
    &bonito_spciconf_readb,
    &bonito_spciconf_readw,
    &bonito_spciconf_readl,
};






/*when the device on the pci bus call dev->irq, pci bus will call pci_bonito_set_irq*/
static void pci_bonito_set_irq(qemu_irq * pic, int irq_num, int level)
{

}

/*map the irq no*/
static int pci_bonito_map_irq(PCIDevice * pci_dev, int irq_num)
{
    exit(1);
    return (0);
}

static void bonito_reset(void *opaque)
{
    bonito_state *s = opaque;

    /* set the default value of north bridge pci config */
    s->config[0x00] = 0x53;
    s->config[0x01] = 0xdf;
    s->config[0x02] = 0xd5;
    s->config[0x03] = 0x00;

    s->config[0x04] = 0x00;
    s->config[0x05] = 0x00;
    s->config[0x06] = 0x00;
    s->config[0x07] = 0x00;

    s->config[0x08] = 0x01;
    s->config[0x09] = 0x00;
    s->config[0x0a] = 0x00;
    s->config[0x0b] = 0x06;

    s->config[0x2c] = 0x00;
    s->config[0x2d] = 0x00;
    s->config[0x2e] = 0x00;
    s->config[0x2f] = 0x00;

    s->config[0x3c] = 0x00;
    s->config[0x3d] = 0x01;
    s->config[0x3e] = 0x3c;
    s->config[0x3f] = 0x00;

    /*set the default value of north bridge registers */

    s->regs[BONITO_BONPONCFG] = 0x1384;
    s->regs[BONITO_IODEVCFG] = 0x2bff8010;
    s->regs[BONITO_SDCFG] = 0x255e0091;

    s->regs[BONITO_GPIODATA] = 0x1ff;
    s->regs[BONITO_GPIOIE] = 0x1ff;
    s->regs[BONITO_DQCFG] = 0x8;
    s->regs[BONITO_DQCFG] = 0x10000000;

    /*set the north bridge register mapping */
    s->bonito_reg_handle = cpu_register_io_memory(0, bonito_read, bonito_write, s);
    s->bonito_reg_start = BONITO_INTERNAL_REG_BASE;
    s->bonito_reg_length = BONITO_INTERNAL_REG_SIZE;
    cpu_register_physical_memory(s->bonito_reg_start, s->bonito_reg_length, s->bonito_reg_handle);


    /*set the north bridge pci configure  mapping */
    s->bonito_pciconf_handle = cpu_register_io_memory(0, bonito_pciconf_read, bonito_pciconf_write, s);
    s->bonito_pciconf_start = BONITO_PCICONFIG_BASE;
    s->bonito_pciconf_length = BONITO_PCICONFIG_SIZE;
    cpu_register_physical_memory(s->bonito_pciconf_start, s->bonito_pciconf_length, s->bonito_pciconf_handle);


    /*set the south bridge pci configure  mapping */
    s->bonito_spciconf_handle = cpu_register_io_memory(0, bonito_spciconf_read, bonito_spciconf_write, s);
    s->bonito_spciconf_start = BONITO_SPCICONFIG_BASE;
    s->bonito_spciconf_length = BONITO_SPCICONFIG_SIZE;
    cpu_register_physical_memory(s->bonito_spciconf_start, s->bonito_spciconf_length, s->bonito_spciconf_handle);

    /*add PCI io space */
    /*PCI IO Space  0x1fd0 0000 - 0x1fd1 0000 */
    if (s->bonito_pciio_length)
    {
        cpu_register_physical_memory(s->bonito_pciio_start, s->bonito_pciio_length, IO_MEM_UNASSIGNED);
    }
    /* Map new IO address */
    s->bonito_pciio_start = BONITO_PCIIO_BASE;
    s->bonito_pciio_length = BONITO_PCIIO_SIZE;
    isa_mem_base = s->bonito_pciio_start;
    isa_mmio_init(s->bonito_pciio_start, s->bonito_pciio_length);

}

PCIBus *bonito_init(qemu_irq * pic)
{

    bonito_state *s;
    s = qemu_mallocz(sizeof(*s));
    assert(s != NULL);
    s->pci = qemu_mallocz(sizeof(*s->pci));
    assert(s->pci != NULL);

    /*get the north bridge pci bus */
    s->pci->bus = pci_register_bus(pci_bonito_set_irq, pci_bonito_map_irq, pic, 0x28, 64);
    /*FIXME: I do not know how to set this config reg. Just set it to 1<<31 because read/write need this bit */
    s->pci->config_reg |= 1u << 31;
    bonito_reset(s);
    return s->pci->bus;

}
